package org.aurora.core.biz;

import org.aurora.core.common.exception.SchedulerStartException;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.*;

import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

@MapperScan("org.aurora.core.biz.persistence")
@SpringBootApplication
public class SchedulerSpringApplication implements ApplicationListener<ApplicationEvent> {

    private static final Logger log = LoggerFactory.getLogger(SchedulerSpringApplication.class);

    private final static AtomicBoolean started = new AtomicBoolean(false);
    private final static AtomicBoolean stopped = new AtomicBoolean(false);

    private final static CountDownLatch awaitStartResultDownLatch = new CountDownLatch(1);
    private static StartStatus startStatus;

    private static final Instance INSTANCE = new Instance();

    private static String configLoadUrl;

    public static Instance getInstance() {
        return INSTANCE;
    }

    private static void startSpringApplication(String configLoadUrl) throws InterruptedException {
        if (started.compareAndSet(false, true)) {
            log.info("Starting Aurora Scheduler Spring Container ......");
            SpringApplication application = new SpringApplication(SchedulerSpringApplication.class);
            // 配置
            SchedulerSpringApplication.configLoadUrl = configLoadUrl;
            application.setDefaultProperties(
                    Collections.singletonMap("spring.config.location", SchedulerSpringApplication.configLoadUrl)
            );
            // 不打印 spring banner
            application.setBannerMode(Banner.Mode.OFF);
            // 启动
            INSTANCE.setSpringApplicationContext(application.run());
            // 等待启动完成 成功/失败
            awaitStartResultDownLatch.await();

            if (startStatus != StartStatus.SUCCESS) {
                throw new SchedulerStartException("Start Aurora Scheduler Spring Container Fail");
            }
        } else {
            log.warn("Aurora Scheduler Spring Container Already Started ......");
        }
    }

    private static void stopSpringApplication() {
        if (stopped.compareAndSet(false, true)) {
            INSTANCE.springApplicationContext.stop();
            log.info("Stopped Aurora Scheduler Spring Container ......");
        }
        log.warn("Aurora Scheduler Spring Container Already Stopped ......");
    }

    /**
     * spring 监听器
     * <a href="https://docs.spring.io/spring-boot/docs/2.7.18/reference/html/features.html#features.spring-application.application-events-and-listeners">...</a>
     * @param event
     */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationFailedEvent) {
            startStatus = StartStatus.FAIL;
            log.error("Start Aurora Scheduler Spring Container Fail ......");
            awaitStartResultDownLatch.countDown();
        } else if (event instanceof ApplicationStartedEvent) {
            startStatus = StartStatus.SUCCESS;
            log.info("Started Aurora Scheduler Spring Container Successfully ......");
            awaitStartResultDownLatch.countDown();
        }
    }

    private enum StartStatus {
        SUCCESS, FAIL
    }

    public static class Instance implements BizBeanAcquirer {
        private ConfigurableApplicationContext springApplicationContext;

        public void setSpringApplicationContext(ConfigurableApplicationContext springApplicationContext) {
            this.springApplicationContext = springApplicationContext;
        }

        @Override
        public <T> T getBean(Class<T> clazz) {
            if (!started.get()) {
                throw new SchedulerStartException("Aurora Scheduler Spring Container Never Started");
            }
            return this.springApplicationContext.getBean(clazz);
        }

        @Override
        public void start(String configLoadUrl) throws InterruptedException {
            startSpringApplication(configLoadUrl);
        }

        @Override
        public void stop() {
            stopSpringApplication();
        }
    }
}
